#region Imported Namespaces
using System;
using System.Collections;
using System.Data;
using System.Data.SqlClient;
using System.Text;

using AgreementsConstants = PHSRAG.Insight21.InsightUtilities.Constants.AgreementsConstants;
using BO = PHSRAG.Insight21.BusinessObjects;
using PHSRAG.Insight21.InsightUtilities;
using DataAccess = PHSRAG.Utility.DataAccess;
using ICachePolicy = PHSRAG.Insight21.Policy.ICachePolicy;
using SearchCriteria = PHSRAG.Insight21.SearchContext.SearchCriteria;
using ExpenseInfoConstants = PHSRAG.Insight21.Agreements.AgreementConstants.ExpenseInfoConstants;
using GenerateKey = PHSRAG.Insight21.BusinessObjects.ModuleKey;
#endregion

namespace PHSRAG.Insight21.Agreements
{
	#region Class ExpenseDetails
	/// <summary>
	/// The ExpenseDetails class supports loading of all Expense details information
	/// for the given fund number and the month ending date.
	/// </summary>
	public class ExpenseDetails:BO.BusinessObject
	{
		#region Constants
		private static string	ExpenseDetailsKey				= GenerateKey.MakeKey("ExpenseDetails_F1EB0573-FE58-4044-B6D5-0B2A72D0FD0F",GenerateKey.Agreement);
		private const string	GetExpenseInfoSPROCName			= "GetExpenseInfo1";
		
		#endregion

		#region Enumerations (Private)
		// Enumerates the arguments required for loadingExpense details.
		private enum LoadMethodArgs
		{
			SearchCriteria,
			FundNumber,
			SessionID,
			Count
		}
		//Enumarated the table index in the datacontainer
		private enum TableIndex
		{
			FundPerMonth,
			Category,
			CategoryTotal,
			Accounts,
			AccountsTotal,
			DirectCost,
			DirectCostBalance,
			IndirectCost,			
			TotalCost,
			Revenue,
			CashBalance,
			ProjectHeader,
			FooterTotals,
			Count
		}
		#endregion

		#region Constructor
		/// <summary>
		/// Parameterized constructor, Initialize the base class with the active cache policy
		/// </summary>
		/// <param name="cachePolicy">Active cache policy</param>
		public ExpenseDetails(ICachePolicy cachePolicy):base(cachePolicy)
		{
		}
		#endregion

		#region Public Properties
		/// <summary>
		/// Get the key to be used in all caching operations for this instance of the object.
		/// </summary>
		public static string CacheKey	{ get { return ExpenseDetailsKey; } }
		#endregion

		#region BusinessObject Methods
		/// <summary>
		/// Cache the current instance using the prevalent cache policy.
		/// </summary>
		public override void Cache()
		{
			CachePolicy.Cache(CacheKey,this);
		}
		/// <summary>
		/// Remove the current instance from the cache using the prevalent cache policy.
		/// </summary>
		public override void UnCache()
		{
			CachePolicy.UnCache(CacheKey);
		}

        private bool IsDate(string sdate)
        {
            try
            {
                DateTime.Parse(sdate);
                return true;
            }
            catch
            {
            }
            return false;
        }

		/// <summary>
		/// Load the current instance withExpense details data for the current user.
		/// </summary>
		/// <param name="connectionString">Database connection string</param>
		/// <param name="args">Is expected to contain the SearchCriteria instance, 
		/// the fund number and the month ending date</param>
		public override void Load(string connectionString, params object[] args)
		{
			if (!args.Length.Equals((int)LoadMethodArgs.Count))
				throw new Exception(String.Format("Expense Details requires {0} parameters", 
					((int)LoadMethodArgs.Count).ToString()));
			try
			{
				SearchCriteria searchCriteria = (SearchCriteria) args[(int)LoadMethodArgs.SearchCriteria];
				string sessionID = (string) args[(int)LoadMethodArgs.SessionID];
				object domainUserID = searchCriteria[Constants.DomainUserIDKey];
				if ((domainUserID == null) || ((string)domainUserID == string.Empty || (string)domainUserID == "0"))
					throw new Exception("Expense Details load requires a domain user ID");

				object includeProgramProjects = searchCriteria[Constants.IncludeProgramProjectsKey];
				bool includeDirect = (includeProgramProjects == null)?true:Convert.ToBoolean(includeProgramProjects);
				string expenseFromDate = searchCriteria[Constants.ExpenseFromDateKey];

                if ((searchCriteria[Constants.ProjectStartDateKey] != null) && !IsDate(searchCriteria[Constants.ProjectStartDateKey]))
                    throw new Exception("Project Start Date contains an invalid date.");

                if ((searchCriteria[Constants.ProjectEndDateKey] != null) && !IsDate(searchCriteria[Constants.ProjectEndDateKey]))
                    throw new Exception("Project End Date contains an invalid date.");
					
                if ((searchCriteria[Constants.BudgetStartDateKey] != null) && !IsDate(searchCriteria[Constants.BudgetStartDateKey]))
                    throw new Exception("Budget Start Date contains an invalid date.");

                if ((searchCriteria[Constants.BudgetEndDateKey] != null) && !IsDate(searchCriteria[Constants.BudgetEndDateKey]))
                    throw new Exception("Budget End Date contains an invalid date.");

                if ((expenseFromDate != null) && !IsDate(expenseFromDate))
                    throw new Exception("Month From Date contains an invalid date.");

                if ((searchCriteria[Constants.ExpenseToDateKey] != null) && !IsDate(searchCriteria[Constants.ExpenseToDateKey]))
                    throw new Exception("Month To Date contains an invalid date.");

                if ((searchCriteria[Constants.ProjectStartDateKey] != null) && (searchCriteria[Constants.ProjectEndDateKey] != null))
                    if (DateTime.Compare(DateTime.Parse(searchCriteria[Constants.ProjectStartDateKey]),DateTime.Parse(searchCriteria[Constants.ProjectEndDateKey])) > 0)
                        throw new Exception("Project Start Date cannot be after Project End Date.");

                if ((searchCriteria[Constants.BudgetStartDateKey] != null) && (searchCriteria[Constants.BudgetEndDateKey] != null))
                    if (DateTime.Compare(DateTime.Parse(searchCriteria[Constants.BudgetStartDateKey]),DateTime.Parse(searchCriteria[Constants.BudgetEndDateKey])) > 0)
                        throw new Exception("Budget Start Date cannot be after Budget End Date.");

                if ((expenseFromDate != null) && (searchCriteria[Constants.ExpenseToDateKey] != null))
                    if (DateTime.Compare(DateTime.Parse(expenseFromDate),DateTime.Parse(searchCriteria[Constants.ExpenseToDateKey])) > 0)
                        throw new Exception("Month From Date cannot be after Month To Date.");

				DataContainer = DataAccess.GetDataSet(connectionString, GetExpenseInfoSPROCName,
					DataAccess.BuildSqlParameterArray(
					"@sessionID", SqlDbType.VarChar, sessionID
					,"@domainUserId", SqlDbType.Int, Convert.ToInt32(domainUserID)
					,"@categoryId",SqlDbType.Int,searchCriteria[Constants.CategoryIDKey]
					,"@accountNumber",SqlDbType.VarChar,searchCriteria[Constants.AccountNumberKey]
					,"@expenseDateFrom",SqlDbType.DateTime, expenseFromDate == null && searchCriteria[Constants.AllExpensesKey] != null ? "1/1/1900" : expenseFromDate
					,"@expenseDateTo",SqlDbType.DateTime, searchCriteria[Constants.ExpenseToDateKey]
					,"@includeProgramProjects", SqlDbType.Bit, includeDirect
					,"@fundNumber", SqlDbType.VarChar, searchCriteria[Constants.FundIDKey]
					,"@folderNumber", SqlDbType.VarChar, searchCriteria[Constants.FolderNumberKey]
					,"@bicsNumber", SqlDbType.VarChar, searchCriteria[Constants.BicsNumberKey]
					,"@walkerNumber", SqlDbType.VarChar, searchCriteria[Constants.WalkerNumberKey]
					,"@sponsorNumber", SqlDbType.VarChar, searchCriteria[Constants.SponsorNumberKey]
					,"@sponsorName", SqlDbType.VarChar, searchCriteria[Constants.SponsorNameKey]
					,"@sponsorTypeId", SqlDbType.Int, searchCriteria[Constants.SponsorTypeIDKey]
					,"@agreementTypeId", SqlDbType.Int, searchCriteria[Constants.AgreementTypeIDKey]
					,"@activityCode", SqlDbType.VarChar, searchCriteria[Constants.ActivityCodeKey]
					,"@locationId", SqlDbType.Int, searchCriteria[Constants.LocationIDKey]
					,"@budgetStartDate", SqlDbType.DateTime, searchCriteria[Constants.BudgetStartDateKey]
					,"@budgetEndDate", SqlDbType.DateTime, searchCriteria[Constants.BudgetEndDateKey]
					,"@projectStartDate", SqlDbType.DateTime, searchCriteria[Constants.ProjectStartDateKey]
					,"@projectEndDate", SqlDbType.DateTime, searchCriteria[Constants.ProjectEndDateKey]
					,"@piId", SqlDbType.Int, searchCriteria[Constants.PIIDKey]
					,"@chiefCodeList", SqlDbType.VarChar, searchCriteria.COSIds
					,"@sponsorGroupTypeId", SqlDbType.Int, searchCriteria[Constants.ViewingOptionKey]
					,"@budgetEndingDays", SqlDbType.Int, searchCriteria[Constants.DaysBudgetEndingKey]
					,"@projectEndingDays", SqlDbType.Int, searchCriteria[Constants.DaysProjectEndingKey]
					,"@fundCreatedDays", SqlDbType.Int, searchCriteria[Constants.DaysFundCreatedKey]
					,"@lowBalance", SqlDbType.Decimal, searchCriteria[Constants.BalanceLessThanKey]
					,"@highBalance", SqlDbType.Decimal, searchCriteria[Constants.BalanceGreaterThanKey]
					,"@fundStatusTypeId", SqlDbType.Int, searchCriteria[Constants.FundStatusIDKey]
					,"@agreementStatusTypeList", SqlDbType.VarChar, searchCriteria[Constants.AgreementStatusIDKey]
					,"@incomingFundNumber",SqlDbType.VarChar,args[(int)LoadMethodArgs.FundNumber]
					,"@monthEndingDate",SqlDbType.DateTime, null
					));
	
				DataSet expenseDetailsData = DataContainer;

				if ( expenseDetailsData.Tables.Count != (int)TableIndex.Count )
					throw new Exception("Failed to load expense details information.");
				else
				{
					expenseDetailsData.Tables[(int)TableIndex.FundPerMonth].TableName =
						ExpenseInfoConstants.FundsPerMonthTableName;
					expenseDetailsData.Tables[(int)TableIndex.Category].TableName =
						AgreementsConstants.CategoryTableName;
					expenseDetailsData.Tables[(int)TableIndex.CategoryTotal].TableName =
						ExpenseInfoConstants.CategoryTotalTableName;
					expenseDetailsData.Tables[(int)TableIndex.Accounts].TableName =
						AgreementsConstants.AccountsTableName;
					expenseDetailsData.Tables[(int)TableIndex.AccountsTotal].TableName =
						ExpenseInfoConstants.AccountTotalTableName;
					expenseDetailsData.Tables[(int)TableIndex.DirectCost].TableName =
						AgreementsConstants.DirectCostsTableName;
					expenseDetailsData.Tables[(int)TableIndex.IndirectCost].TableName =
						AgreementsConstants.IndirectCostsTableName;
					expenseDetailsData.Tables[(int)TableIndex.DirectCostBalance].TableName =
						ExpenseInfoConstants.DirectCostBalanceTableName;
					expenseDetailsData.Tables[(int)TableIndex.TotalCost].TableName =
						AgreementsConstants.TotalCostsTableName;
					expenseDetailsData.Tables[(int)TableIndex.Revenue].TableName =
						AgreementsConstants.RevenueTableName;
					expenseDetailsData.Tables[(int)TableIndex.CashBalance].TableName =
						AgreementsConstants.CashBalanceTableName;
					expenseDetailsData.Tables[(int)TableIndex.ProjectHeader].TableName =
						AgreementsConstants.ProjectHeader;
					expenseDetailsData.Tables[(int)TableIndex.FooterTotals].TableName =
						ExpenseInfoConstants.FooterTotalsTableName;

                    // we're no longer using the direct cost balance table, so take it out for now
                    expenseDetailsData.Tables.Remove(ExpenseInfoConstants.DirectCostBalanceTableName);
                }
			}
			catch(Exception ex)
			{
				throw new Exception(@"Error in loading expense Details",ex);
			}
		}

		/// <summary>
		/// Load the current instance with category account data for the current user and category clicked.
		/// </summary>
		/// <param name="connectionString">Database connection string</param>
		/// <param name="args">Is expected to contain the SearchCriteria instance
		/// The search critera instance is expeceted to contain atleast the domain user id</param>
		public void LoadCategoryAccounts(string connectionString, params object[] args)
		{
			if (args.Length != 4)
				throw new Exception("Expense Details load requires SearchCriteria to be specified");

			SearchCriteria searchCriteria = (SearchCriteria) args[0];
			string sessionID = (string) args[1];
			int categoryID = (int) args[2];
			string fundNumber = (string) args[3];

			System.Diagnostics.Trace.WriteLine(string.Format("{0}: ExpenseDetails: SC {1}", DateTime.Now, searchCriteria));

			if ((searchCriteria[Constants.ExpenseFromDateKey] != null) && !IsDate(searchCriteria[Constants.ExpenseFromDateKey]))
				throw new Exception("Month From Date contains an invalid date.");

			if ((searchCriteria[Constants.ExpenseToDateKey] != null) && !IsDate(searchCriteria[Constants.ExpenseToDateKey]))
				throw new Exception("Month To Date contains an invalid date.");

			if ((searchCriteria[Constants.ExpenseFromDateKey] != null) && (searchCriteria[Constants.ExpenseToDateKey] != null))
				if (DateTime.Compare(DateTime.Parse(searchCriteria[Constants.ExpenseFromDateKey]),DateTime.Parse(searchCriteria[Constants.ExpenseToDateKey])) > 0)
					throw new Exception("Month From Date cannot be after Month To Date.");

			DataSet accountTables = DataAccess.GetDataSet(connectionString, "GetExpenseInfo2",
				DataAccess.BuildSqlParameterArray(
				"@SessionID", SqlDbType.VarChar, sessionID,
				"@domainUserId", SqlDbType.Int, searchCriteria[Constants.DomainUserIDKey],
				"@categoryId", SqlDbType.Int, categoryID,
				"@accountNumber",SqlDbType.VarChar,searchCriteria[Constants.AccountNumberKey],
				"@expenseDateFrom", SqlDbType.DateTime, searchCriteria[Constants.ExpenseFromDateKey],
				"@expenseDateTo", SqlDbType.DateTime, searchCriteria[Constants.ExpenseToDateKey],
				"@incomingFundNumber",SqlDbType.VarChar,fundNumber
				));


			if (accountTables.Tables.Count != 2)
				throw new Exception("Failed to load account information.");

			accountTables.Tables[0].TableName = AgreementsConstants.AccountsTableName;
			accountTables.Tables[1].TableName = ExpenseInfoConstants.AccountTotalTableName;

			DataSet expenseDetailsData = DataContainer;
			if (expenseDetailsData.Tables.Contains(AgreementsConstants.AccountsTableName) )
			{
				DataTable accounts = expenseDetailsData.Tables[AgreementsConstants.AccountsTableName];
                accounts.Clear();
				foreach (DataRow dataRow in accountTables.Tables[AgreementsConstants.AccountsTableName].Rows)
					accounts.ImportRow(dataRow);

				DataTable accountTotals = expenseDetailsData.Tables[ExpenseInfoConstants.AccountTotalTableName];
                accountTotals.Clear();
				foreach (DataRow dataRow in accountTables.Tables[ExpenseInfoConstants.AccountTotalTableName].Rows)
					accountTotals.ImportRow(dataRow);
			}
			else 
			{
				expenseDetailsData.Tables.Add(accountTables.Tables[AgreementsConstants.AccountsTableName]);
				expenseDetailsData.Tables.Add(accountTables.Tables[ExpenseInfoConstants.AccountTotalTableName]);
			}

		}
		#endregion
	}
	#endregion
}